﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using X.Infrastructure.Shapes;
using X.Infrastructure.WPF.Extends;
using X.WPF.Controls.Enums;
using X.WPF.Controls.EventArgs.CanvasShapeArgs;
using X.WPF.Controls.EventArgs.DraggableControlArgs;
using Panel = System.Windows.Controls.Panel;

namespace X.WPF.Controls.CustomControls
{
    /// <summary>
    /// 画布多边形控件
    /// </summary>
    public class CanvasPolygon : CanvasShape
    {
        #region # 字段及构造器

        /// <summary>
        /// 点位控件列表
        /// </summary>
        private readonly ObservableCollection<Ellipse> _pointElements;

        /// <summary>
        /// 是否绘制
        /// </summary>
        private bool _isDraw;

        /// <summary>
        /// 静态构造器
        /// </summary>
        static CanvasPolygon()
        {
            CanvasPolygon.PointsProperty = DependencyProperty.Register(nameof(CanvasPolygon.Points), typeof(ObservableCollection<PointX>), typeof(CanvasPolygon), new PropertyMetadata(new ObservableCollection<PointX>(), CanvasPolygon.OnPointsChanged));
        }

        /// <summary>
        /// 默认构造器
        /// </summary>
        public CanvasPolygon()
            : base(CanvasShapeType.Polygon)
        {
            this._pointElements = new ObservableCollection<Ellipse>();
            this.Points = new ObservableCollection<PointX>();
        }

        #endregion

        #region # 属性

        #region 点位列表 —— ObservableCollection<PointX> Points

        /// <summary>
        /// 点位列表依赖属性
        /// </summary>
        public static DependencyProperty PointsProperty;

        /// <summary>
        /// 点位列表
        /// </summary>
        public ObservableCollection<PointX> Points
        {
            get => (ObservableCollection<PointX>)base.GetValue(CanvasPolygon.PointsProperty);
            set => base.SetValue(CanvasPolygon.PointsProperty, value);
        }

        #endregion

        #endregion

        #region # 依赖属性 

        #endregion

        #region # 回调方法

        #region 点位列表改变回调方法 —— static void OnPointsChanged(DependencyObject dependencyObject...
        /// <summary>
        /// 点位列表改变回调方法
        /// </summary>
        private static void OnPointsChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs eventArgs)
        {
            CanvasPolygon canvasPolygon = (CanvasPolygon)dependencyObject;

        }
        #endregion 

        #endregion

        #region # 路由事件

        #endregion

        #region # 事件处理程序

        #region 画布鼠标左键按下事件 —— void OnCanvasMouseLeftButtonDown(object sender...
        /// <summary>
        /// 画布鼠标左键按下事件
        /// </summary>
        private void OnCanvasMouseLeftButtonDown(object sender, MouseButtonEventArgs eventArgs)
        {
            if (this._parentCanvas == null)
                return;

            PointX position = eventArgs.GetPosition(this._parentCanvas).ToPointX();
            this.PointRender(position);
        }
        #endregion

        #region 画布鼠标右键按下事件 —— void OnCanvasMouseRightButtonDown(object sender...
        /// <summary>
        /// 画布鼠标右键按下事件
        /// </summary>
        private void OnCanvasMouseRightButtonDown(object sender, MouseButtonEventArgs eventArgs)
        {
            this._isDraw = false;
            if (this._parentCanvas == null)
                return;

            bool result = this.PolygonRender();

            this._parentCanvas.MouseLeftButtonDown -= this.OnCanvasMouseLeftButtonDown;
            this._parentCanvas.MouseRightButtonDown -= this.OnCanvasMouseRightButtonDown;
            if (result)
            {
                CanvasShapeElementDrawnEventArgs areArgs = new CanvasShapeElementDrawnEventArgs(CanvasShape._ShapeDrawnEvent, this, this);
                this.RaiseEvent(areArgs);

                if (!areArgs.IsCancel)
                    return;

                this.DrawCancel();
            }
            else
            {
                this._parentCanvas.Children.Remove(this);
                this.RaiseEvent(new CanvasShapeElementDrawCancelEventArgs(CanvasShape._ShapeDrawCancelEvent, this, this));
            }
        }
        #endregion

        #region 多边形鼠标按下事件 —— void OnCircleMouseDown(object sender, MouseEventArgs eventArgs)
        /// <summary>
        /// 多边形鼠标按下事件
        /// </summary>
        private void OnCircleMouseDown(object sender, MouseEventArgs eventArgs)
        {
            // 挂起路由事件
            this.RaiseEvent(new CanvasShapeElementClickEventArgs(CanvasShape._ShapeClickEvent, this, null, this));
        }
        #endregion

        #endregion

        #region # 方法

        // Override

        #region 绘制 —— override void Draw(PointX startPoint)
        /// <summary>
        /// 绘制
        /// </summary>
        /// <param name="startPoint">起点</param>
        public override void Draw(PointX startPoint)
        {
            if (this._parentCanvas == null)
                return;

            this._parentCanvas.MouseLeftButtonDown += this.OnCanvasMouseLeftButtonDown;
            this._parentCanvas.MouseRightButtonDown += this.OnCanvasMouseRightButtonDown;
            this._isDraw = true;

            this.PointRender(startPoint);
        }
        #endregion

        #region 绘制 —— override void Draw(double scale, ShapeX shapeX)
        /// <summary>
        /// 绘制
        /// </summary>
        public override void Draw(double scale, ShapeX shapeX)
        {
            PolygonX polygonX = (PolygonX)shapeX;

            IEnumerable<PointX> points = polygonX.Points.Select(i =>
            {
                double x = i.X * scale;
                double y = i.Y * scale;
                PointX point = new PointX(x, y);

                this.PointRender(point);

                return point;
            });

            this.Points = new ObservableCollection<PointX>(points);
            this.Name = polygonX.Name;
            this.Tag = polygonX.Tag;

            this.PolygonRender();
        }
        #endregion

        #region 绘制取消 —— abstract void DrawCancel()
        /// <summary>
        /// 绘制取消
        /// </summary> 
        public override void DrawCancel()
        {
            foreach (Ellipse pointElement in this._pointElements)
            {
                this._parentCanvas?.Children.Remove(pointElement);
            }

            base.DrawCancel();
        }
        #endregion

        #region 拖拽 —— override void Drag(DraggedEventArgs draggedEventArgs)
        /// <summary>
        /// 拖拽
        /// </summary> 
        /// <param name="draggedEventArgs">拖拽参数</param>
        public override void Drag(DraggedEventArgs draggedEventArgs)
        {
        }
        #endregion

        #region 初始化形状控件 —— void InitShapeElement()
        /// <summary>
        /// 初始化形状控件
        /// </summary>
        protected override Shape InitShapeElement()
        {
            Polygon polygon = new Polygon();
            polygon.MouseDown += this.OnCircleMouseDown;

            return polygon;
        }
        #endregion


        // Private 

        #region 渲染点 —— void PointRender(PointX position)
        /// <summary>
        /// 渲染点
        /// </summary>
        /// <param name="position">位置</param>
        private void PointRender(PointX position)
        {
            int radius = 3;

            Brush colorBrush = this.Stroke;
            if (this._isDraw && this._pointElements.Count == 0)
            {
                colorBrush = new SolidColorBrush(Colors.Yellow);
            }

            Ellipse pointElement = new Ellipse
            {
                Width = radius * 2,
                Height = radius * 2,
                StrokeThickness = 1,
                Fill = colorBrush,
                Stroke = colorBrush
            };
            Canvas.SetLeft(pointElement, position.X - radius);
            Canvas.SetTop(pointElement, position.Y - radius);
            Panel.SetZIndex(pointElement, this.ZIndex - 1);

            this._pointElements.Add(pointElement);
            this._parentCanvas.Children.Add(pointElement);

            this.Points.Add(position);
        }
        #endregion

        #region 渲染点位列表 —— void PointsRender()
        /// <summary>
        /// 渲染点位列表
        /// </summary>
        private void PointsRender()
        {

            foreach (Ellipse pointElement in this._pointElements)
            {
                // 绑定画笔
                Binding strokeBinding = new Binding(nameof(this.Stroke))
                {
                    Source = this,
                    Mode = BindingMode.TwoWay
                };
                pointElement.SetBinding(Shape.StrokeProperty, strokeBinding);
                pointElement.SetValue(Shape.StrokeProperty, this.Stroke);

                // 绑定画笔粗细
                Binding strokeThicknessBinding = new Binding(nameof(this.StrokeThickness))
                {
                    Source = this,
                    Mode = BindingMode.TwoWay
                };
                pointElement.SetBinding(Shape.StrokeThicknessProperty, strokeThicknessBinding);
                pointElement.SetValue(Shape.StrokeThicknessProperty, this.StrokeThickness);

                // 绑定填充颜色
                Binding fillBinding = new Binding(nameof(this.Fill))
                {
                    Source = this,
                    Mode = BindingMode.TwoWay
                };
                pointElement.SetBinding(Shape.FillProperty, fillBinding);
                pointElement.SetValue(Shape.FillProperty, this.Fill);
            }
        }
        #endregion

        #region 渲染多边形 —— bool PolygonRender()
        /// <summary>
        /// 渲染多边形
        /// </summary>
        private bool PolygonRender()
        {
            if (this.Points.Count < 2)
                return false;

            if (!this._parentCanvas.Children.Contains(this.ShapeElement))
                this._parentCanvas.Children.Add(this.ShapeElement);

            // 重新排序点位
            this.SortPoints();

            // 绘制点
            PointCollection points = new PointCollection();
            foreach (PointX polyPoint in this.Points)
            {
                Point point = polyPoint.ToPoint();
                points.Add(point);
            }

            // 绘制多边形
            this.ShapeElement.SetValue(Polygon.PointsProperty, points);
            Panel.SetZIndex(this.ShapeElement, this.ZIndex);

            // 渲染点位列表
            this.PointsRender();

            return true;
        }
        #endregion

        #region 点集排序 —— void SortPoints()
        /// <summary>
        /// 点集排序
        /// </summary>
        /// <returns>已排序点集</returns>
        public void SortPoints()
        {
            IList<PointX> sortedPoints = this.Points;

            //计算重心
            double x = 0, y = 0;
            foreach (PointX point in sortedPoints)
            {
                x += point.X;
                y += point.Y;
            }
            PointX center = new PointX(x / sortedPoints.Count, y / sortedPoints.Count);

            //冒泡排序
            for (int i = 0; i < sortedPoints.Count - 1; i++)
            {
                for (int j = 0; j < sortedPoints.Count - i - 1; j++)
                {
                    if (!this.ComparePoints(sortedPoints[j], sortedPoints[j + 1], center))
                        continue;

                    sortedPoints[j] = sortedPoints[j + 1];
                    sortedPoints[j + 1] = sortedPoints[j];
                }
            }

            this.Points = new ObservableCollection<PointX>(sortedPoints);
        }
        #endregion

        #region 比较两个点 —— bool ComparePoints(PointX pointA...
        /// <summary>
        /// 比较两个点
        /// </summary>
        /// <param name="pointA">点A</param>
        /// <param name="pointB">点B</param>
        /// <param name="corePoint">重心点</param>
        /// <returns>若点a大于点b（即点a在点b顺时针方向）返回true，否则返回false</returns>
        private bool ComparePoints(PointX pointA, PointX pointB, PointX corePoint)
        {
            if (pointA.X >= 0 && pointB.X < 0)
            {
                return true;
            }
            if (pointA.X == 0 && pointB.X == 0)
            {
                return pointA.Y > pointB.Y;
            }

            //向量OA和向量OB的叉积
            double det = (pointA.X - corePoint.X) * (pointB.Y - corePoint.Y) - (pointB.X - corePoint.X) * (pointA.Y - corePoint.Y);
            if (det < 0)
            {
                return true;
            }
            if (det > 0)
            {
                return false;
            }

            //向量OA和向量OB共线，以距离判断大小
            double d1 = (pointA.X - corePoint.X) * (pointA.X - corePoint.X) + (pointA.Y - corePoint.Y) * (pointA.Y - corePoint.Y);
            double d2 = (pointB.X - corePoint.X) * (pointB.X - corePoint.X) + (pointB.Y - corePoint.Y) * (pointB.Y - corePoint.Y);

            return d1 > d2;
        }
        #endregion

        #endregion
    }
}